module dungeon.stats;

import std.string;

import nade.ast;
import nade.loader;

import dungeon.formula;


class Stats {
	this()
	{
		_values.length = StatInfo.count;
		_values[] = 0;
	}

	bool checkMin(Stats rhs)
	{
		foreach(k, value; rhs._values)
			if(this[k] < value)
				return false;

		return true;
	}
	bool checkMax(Stats rhs)
	{
		foreach(k, value; rhs._values)
			if(this[k] > value)
				return false;

		return true;
	}

	real add(uint handle, real value) { return _values[handle] += value; }
	real add(string id, real value) { return add(StatInfo.get(id).handle, value); }

	real evaluate(uint handle) { return StatInfo.evaluate(this, handle); }
	real evaluate(string id) { return evaluate(StatInfo.get(id).handle); }

	real opIndex(uint handle) { return _values[handle]; }
	real opIndexAssign(real rhs, uint handle) { return _values[handle] = rhs; }
	real opIndex(string id) { return opIndex(StatInfo.get(id).handle); }
	real opIndexAssign(real rhs, string id) { return opIndexAssign(rhs, StatInfo.get(id).handle); }

	Stats opAdd(Stats rhs)
	{
		auto result = new Stats;
		for(uint i = 0;  i < StatInfo.count;  ++i) result[i] = this[i] + rhs[i];
		return result;
	}

	int opApply(int delegate(ref real) dg)
	{
		foreach(ref value; _values)
		{
			const d = dg(value);
			if(d != 0)
				return d;
		}

		return 0;
	}
	int opApplyReverse(int delegate(ref real) dg)
	{
		foreach_reverse(ref value; _values)
		{
			const d = dg(value);
			if(d != 0)
				return d;
		}

		return 0;
	}

	real[] values() { return _values; }

	protected {
		real[]		_values;
	}
}


class StatInfo {
	this()
	{
		_handle = _byHandle.length;
		_byHandle ~= this;
	}

	uint handle() { return _handle; }
	string name() { return _name; }
	string id() { return _id; }

	static {
		StatInfo get(uint handle) { return _byHandle[handle]; }
		StatInfo get(string id)
		{
			auto p = id in _byId;
			if(p is null) return null;
			return *p;
		}

		real evaluate(Stats stats, uint handle)
		{
			return _byHandle[handle]._formula.evaluate(stats);
		}

		uint count() { return _byId.length; }

		protected static {
			StatInfo[]			_byHandle;
			StatInfo[string]	_byId;
		}
	}

	protected {
		uint			_handle;
		string			_id;
		string			_name;
		Formula			_formula = null;
	}

	static void install() {
		Loader!(StatInfo).add("formula",
			(ref StatInfo stat, AST ast, Object[string] state)
			{
				stat._formula = new Formula(ast);
			}
		);

		Loader!(StatInfo).add("name",
			(ref StatInfo stat, AST ast, Object[string] state)
			{
				stat._id = ast[0].as!string;
				stat._name = ast[1].as!string;

				_byId[stat._id] = stat;
			}
		);
	}
}
